Skip to content

Adopt the Sonar/Checkstyle/FindBugs ruff rule-family bar; fix the src/ baseline - #102

Merged
open-coder-ai merged 24 commits into
mainfrom
w50/lint-adoption
Sep 2, 2026
Merged

Adopt the Sonar/Checkstyle/FindBugs ruff rule-family bar; fix the src/ baseline#102
open-coder-ai merged 24 commits into
mainfrom
w50/lint-adoption

Conversation

@open-coder-ai

Copy link
Copy Markdown
Owner

What

Adopts the owner's static-analysis standard (plan/coding-standards.md §3): turns on
C90 N PLR PLW PLC ERA T20 ARG RET SIM PIE FBT A B S BLE TRY RUF in [tool.ruff.lint] select
(mccabe max-complexity 10, pylint max-args 5 / max-branches 12 / max-returns 6 / max-statements 50),
and fixes the resulting src/ baseline (re-measured at 503 findings on this branch's starting
point) category by category, in bisectable commits, never a blanket --fix. Adds a new
tools/check_literal_duplication.py (+ test) implementing the Sonar S1192 "magic string
repeated" check the brief calls for, and records the standard in AGENTS.md's hard rules
beside code_comments/externalized_text.

ruff check src/ is fully clean except one deliberately deferred category (see below).

Category-by-category summary (see individual commits for detail)

  • Mechanical/safe (RUF100/022, SIM105, B905, PLC0207, PLR0402, SIM300, etc.):
    contextlib.suppress, explicit zip(strict=), stale-noqa cleanup — one commit fixed a
    self-inflicted mistake in the very next commit (see "Correct the previous commit" — a
    ruff check --select X --fix invocation implicitly narrows the active rule set to X,
    which made RUF100 wrongly call several still-needed noqa comments unused).
  • Magic values (PLR2004): named module constants for every bare comparison.
  • Literal duplication: new AST-based check + test; every finding fixed by hoisting a
    shared constant or a repeated block into one function — no allowlist entries were needed.
  • Exception hygiene (TRY003, BLE001, S110): messages extracted to a local before
    raise; remaining broad except Exception sites either narrowed or noqa-justified as
    deliberate best-effort logging/fetch contracts.
  • Boolean traps (FBT001/002/003): every boolean parameter moved after * (keyword-only);
    every call site updated, including the vendored gate runtime (goldens regenerated).
  • Unused arguments (ARG001): interface-conformance params prefixed _; one genuinely
    dead parameter deleted per the repo's YAGNI rule.
  • Asserts (S101): all five are internal invariants over trusted state, not request
    validation — noqa-justified rather than rewritten.
  • Subprocess hardening (PLW1510, S603, S607): explicit check=False where already
    implicit; every literal "git" resolved via shutil.which; remaining S603 sites
    noqa-justified where running a subprocess is the feature itself.
  • Lazy imports (PLC0415): 65 of 71 hoisted to module top after verifying no circular
    import (a fresh python -c "import chock.<module>" per file); the rest stayed lazy for a
    real reason each documents — two genuine import cycles, two sites the test suite patches via
    monkeypatch.setattr(source_module, "name", fake) (only works with a fresh per-call import),
    and lifecycle.py/scaffold/remove.py at file scope for the same reason across nine
    patched call sites in tests/test_lifecycle.py and tests/test_remove.py.
  • print → output surface (T201): new chock/output.py (warn/error) absorbs the 31
    call sites that matched the [WARN]/[ERROR]-to-stderr convention exactly; the other ~183
    are per-file-ignored as genuine CLI/render output — chock is its CLI, every flagged file is
    an argparse *_main()/main() dispatcher or a report renderer, and there is no separate
    consumer of these strings to route through an indirection layer.

Deferred: complexity (C901/PLR091x)

37 findings across 21 functions need an actual split or argument-bundling refactor, which
the brief calls out as its own careful, behaviour-preservation-checked step — not attempted
in this same pass. Deferred via scoped per-file-ignores marked TODO(lint-adoption)
(never a blanket ignore), each naming the offending function; full enumeration in the w50
report (plan/spine-a/reports/w50.md, org-plan).

tests/, acceptance/ and tools/ still carry ~423 findings under the new rule set beyond
their existing S101/FBT/PLR2004 carve-out — out of scope: the brief's "fix the baseline"
mandate and its measured count are both specifically about src/.

Behaviour preservation

  • Full suite green throughout (1055 passed, 2 skipped at HEAD).
  • chock check, chock check --only matrix, chock check --only verify, chock sync --repo . --check,
    ruff check ., ruff format --check . all clean.
  • pytest acceptance/ (21) and pytest acceptance/test_isolation.py (2) both green.
  • Full-artifact before/after diff (.chock/compiled/, coverage.json, chock.lock,
    INDEX.md, all vs. the branch point) is byte-identical everywhere except the vendored
    .chock/bin/* runtimes, whose source (gate/runner.py, guard_runner.py,
    sessionstart.py) this PR intentionally edited — every one of those diffs traces to a
    specific, documented fix (keyword-only params, resolved git path, hoisted constants);
    goldens regenerated with CHOCK_REGEN_GOLDENS=1 and committed.

Definition of done

  • chock check → 0 errors, 0 warnings, 0 infos
  • chock check --only matrix passes (no behavior change)
  • chock sync --repo . --check clean
  • chock check --only verify clean
  • Registry rescanned; no stale entries
  • pytest -q green
  • pytest acceptance/ green
  • ruff check . and ruff format --check . clean (on the covered scope; see "Deferred" above)

Claims

  • No surface is described as enforcing more than it installs — nothing here changes what
    is emitted or installed; INSTALLED_SURFACES, the coverage table and
    docs/enforcement-surfaces.md are all unaffected.

🤖 Generated with Claude Code


Generated by Claude Code

Turn on C90/N/PLR/PLW/PLC/ERA/T20/ARG/RET/SIM/PIE/FBT/A/B/S/BLE/TRY/RUF
in [tool.ruff.lint] select per plan/coding-standards.md §3, with
mccabe max-complexity=10 and pylint max-args=5/max-branches=12/
max-returns=6/max-statements=50. tests/ and acceptance/ get a
documented per-file-ignore for S101 (bare assert by design), FBT
(fixture values as positional args) and PLR2004 (literal test data,
not magic values).

The baseline this select turns up is fixed category by category in
the commits that follow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
RUF100 (noqa comments no longer needed once the new rule set proved
them unnecessary -- ruff confirmed F401/BLE001 don't fire on the
underlying lines), RUF022 (__all__ sort order), PLC0207 (str.split
needs maxsplit), PLR0402 (manual from-import for
importlib.resources), SIM300 (yoda-condition reorder). All via
targeted `ruff check --select <rule> --fix`, reviewed individually,
never a blanket --fix. Full suite green (1036 passed, 6 skipped).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Replace try/except-pass patterns with contextlib.suppress(...), and
narrow to OSError where the guarded call (chmod, rmdir) only ever
raises that (was a broad `except Exception` in two installers.py
spots). zip() over paired same-length tuples in toggles.py now
declares strict=True. Full suite green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…gged

The prior commit ran `ruff check --select RUF100,... --fix`, and a CLI
--select overrides the project's configured select entirely for that
invocation -- so RUF100 evaluated "unused" against a rule set of just
those five codes, not the full configured bar, and treated every
noqa:F401/noqa:BLE001 as unused because F401/BLE001 weren't even being
checked in that run. Restores the six genuinely-needed noqa comments
(facade re-export F401s in registry/__init__.py and
validation/__init__.py, and BLE001 on three deliberate best-effort
except-Exception blocks in autocompile.py and scaffold/recompile.py)
and keeps the two RUF100 removals that verify as genuinely unused
under the full project config (hooks/install.py's F401, covered by an
__all__; scaffold/recompile.py's BLE001 on a block that re-raises,
which BLE001 doesn't flag as blind). Verified against `ruff check src/`
with no CLI --select override. Full suite green.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaced bare int/word-count comparisons (row/path segment counts,
gate exit codes, name/paragraph/line-length/staleness thresholds)
with named module-level constants documenting what the number means,
across authoring/matrix.py, eval/execute.py, the vendored gate
runner, manifest.py, plugin/marketplace.py,
validation/checks_content.py and validation/frontier.py. Resynced the
vendored runtime copy. Full suite green.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New tools/check_literal_duplication.py: an AST-based Sonar S1192-style
check flagging any non-docstring string constant >= 20 chars that
repeats >= 3 times outside data/templates/tests, with
tools/literal_duplication_allowlist.json as the escape hatch (every
entry must carry a non-empty reason -- enforced by
load_allowlist()). tests/test_literal_duplication.py covers the
check's own behavior and asserts src/ is clean under it.

Fixed every literal the check found on src/ by extracting a shared
constant or hoisting a repeated block into a function, rather than
allowlisting: BUDGETS-key lookups cached to a local once per function
(checks_evals.py, checks_content.py); manifest.schema.json,
content_instructions, determinization_reviewed and
agent_specific_vocabulary centralized in manifest.py/loading.py and
imported where checked; three emitters' identical
package_data_dir call hoisted to one shared DATA_DIR in the package
init; three near-identical scan-registry-and-report blocks in
scaffold/init.py, new.py and skills.py replaced by one
chock.registry.core.rescan_and_report(); the '--agents requires...'
CLI message centralized in compile/surfaces.py; gate/schema.py's four
schema objects share one _CLOSED_OBJECT fragment;
dependency_allowlist/forbidden_path_regex deduplicated between
gate/schema.py and eval/derive.py, leaving the vendored gate runner's
own copy as the one deliberate exception (it cannot import its
framework-side siblings and stay stdlib-only).

No allowlist entries were needed -- everything the check found had a
natural single owner once written that way. Resynced the vendored gate
runtime after editing its source. Full suite green (1045 passed, 6
skipped); chock check and sync --check both pass.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
37 raise-vanilla-args findings across 17 files, all the same shape --
a long f-string or literal built inline in the raise call. Extracted
each to a 'msg = ...' assignment immediately before the raise, which
keeps the exact same exception type and message while satisfying the
rule (message construction is no longer inline at the raise site).
No new exception classes were needed; every site already raised a
purpose-built or stdlib exception type.

Also fixed a stray I001 import-sort left over from an earlier commit,
and trimmed hooks/in_agent_install.py by one line (a redundant early
return the TRY003 fix pushed past the file's own 300-line budget --
the set comprehension already returns empty when nothing is
installed, so the guard added nothing).

Full suite green (1045 passed, 6 skipped); chock check and sync
--check both pass; literal-duplication check still clean.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three best-effort logging/fetch sites (guard_runner.py, gate runner's
own outcome logger and dependency extractor, frontier_ingest.py's
fetch_url) keep their broad except on purpose -- their own docstrings
already say 'best effort, never raises' -- so each gets a
noqa: BLE001 naming that contract. validation/frontier.py's
except-Exception-pass narrows to (ValueError, TypeError), the actual
failure modes of datetime.fromisoformat on a malformed timestamp; that
also clears S110, which only flags a bare/blind except-pass.

Resynced the vendored gate runtime. Full suite green (1045 passed, 6
skipped); chock check, sync --check and the literal-duplication check
all pass.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every boolean-typed function parameter across src/ (disabled, verbose,
blocked, has_extended, enforced, allow_empty, force, agent_agnostic,
skip_hooks, use_network, use_json) moves after a bare '*' so it can
only be passed by keyword; every call site updated to match, including
tests/ and the vendored gate runtime (guard_runner.py's log_outcome,
composed into the runtime bundle -- goldens regenerated with
CHOCK_REGEN_GOLDENS=1 and the repo's own vendored copies resynced).
The handful of FBT003 call-site findings were Grade(..., False)
NamedTuple constructions; the 'witnessed' field is now passed by
keyword there too.

Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Most findings are shared dispatch-table signatures (validation
check_*(artifact_dir, manifest, artifact_type, report), emitter
emit(policy_dir, output_dir, manifest), gate _kind_*(ctx, params,
event)) where a given implementation legitimately ignores an argument
every sibling in the table receives; each is prefixed with '_'
(all call sites are positional, so this is a no-op at the call site).
Same treatment for installers.install_validate_hook's repo_root,
gateway/gates.evaluate's tool_name, and registry/core's
extract_dependencies artifact param.

lock.py's build_lock(repo_root, source_root=None) is different:
source_root is dead -- no caller anywhere passes it, it is not
exported or documented as public API, and the function ignores it
entirely -- so it is deleted rather than prefixed, per the repo's own
YAGNI rule.

Regenerated goldens/resynced after gate/runner.py's two _kind_*
renames. Full suite green (1045 passed, 6 skipped); chock check, sync
--check and the literal-duplication check all pass.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All five are import-time or type-narrowing invariants over internal
state or build-time constants -- never over untrusted request input
-- so a noqa is the right tool, not a raise rewrite that would lose
the type-checker narrowing (proxy.py, plugin/cli.py) or add ceremony
around a same-module sanity check (in_agent.py, plugin/copilot.py,
plugin/store.py). Each noqa names why. Full suite green.

Signed-off-by: Claude <noreply@anthropic.com>

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PLW1510: every subprocess.run() that already branches on
proc.returncode gets an explicit check=False -- the current implicit
default, just stated.

S607: every literal "git" argv resolved once per module via
shutil.which("git") or "git" (falls back to the bare name if
resolution fails, same as today). Since ruff can no longer see a
literal partial path, this also flips the finding to S603 at several
sites; each of those, plus every subprocess call whose whole job is
running a configured or user-invoked command (a gateway's downstream
MCP server, a guard script under test, a repo-registered review
check, git-catalog fetch for chock add), gets a noqa: S603 naming
why running a subprocess is the feature there, not a hardening gap.

The vendored gate runtime's guard_runner.py and sessionstart.py both
gained a `shutil` reference; runtime_bundle.py's compositor only
carries a fixed, explicit import/rename set into the bundle (see its
_RENAME dict and data/imports.py.tmpl), so shutil needed adding to
both -- caught by test_pretooluse*/test_sessionstart_arm* failing
with 'NameError: shutil' inside the composed runtime, not by ruff.
Goldens regenerated, repo resynced.

Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…rts lazy

Of the 71 findings, 65 moved to module top level after verifying no
circular import exists (checked by grepping each target module for a
back-reference, then confirming with a fresh `python -c "import
chock.<module>"` per file plus a full `import chock.cli` sanity check
that exercises the CLI's own dynamic dispatch).

Two genuine import cycles turned up this way and stayed lazy, each
with a one-line comment plus inline noqa naming the cycle:
validation/checks_drift.py's compiled_differences (-> scaffold.recompile
-> registry.core -> registry.cli -> validation.report -> validation
package init -> checks_drift, a cycle) and index/builder.py's
discover_artifacts (-> validation.loading -> validation package init
-> checks_repo -> index.builder, a cycle).

Two more went back to lazy after the full suite caught it, not ruff:
scaffold/recompile.py's build_lock/write_lock and
hooks/autocompile.py's compile_policy are patched in tests
(test_adopter_safety.py, test_engine_scan.py, test_agent_selection.py)
via monkeypatch.setattr(source_module, "name", fake) -- a pattern that
only works if the caller re-imports the name fresh on every call
rather than binding it once at module load. Each site keeps its lazy
import with a comment explaining why and an inline noqa.

lifecycle.py and scaffold/remove.py stay fully lazy (per-file-ignore
in pyproject.toml) for the same reason at file scale:
tests/test_lifecycle.py and tests/test_remove.py patch nine different
source modules this way across their test cases -- rewriting all of
them to target the new binding location would be a bigger, riskier
change than the lint finding warrants.

One more real bug this surfaced: the vendored gate runtime's
sessionstart.py needed importlib.util.find_spec, a dotted attribute
chain the bundle compositor's Name-only renamer (runtime_bundle.py)
can't rewrite -- attempting to hoist it produced a NameError inside
the composed runtime, caught by test_sessionstart_arm.py, not ruff.
Left lazy with a comment; guard_runner.py's json import and
gate/build.py's chock.manifest import hoisted cleanly since neither
needed bundle-compositor support (json ships in the bundle's fixed
top-import set already; gate/build.py isn't vendored at all).

Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
New chock/output.py: warn(message) and error(message), each owning
the "[WARN] "/"[ERROR] " prefix and the file=sys.stderr routing in one
place. Migrated the 31 call sites across 16 files that matched the
convention exactly (print(f"[WARN] ...", file=sys.stderr) or the
[ERROR] equivalent) -- every one verified byte-identical (the helper
emits the exact same prefix and stream). Three near-matches were left
alone because they print to stdout, not stderr, and routing them
through warn()/error() would have silently changed which stream the
message lands on: registry/cli.py's finding line, registry/core.py's
two manifest-scan messages, and scaffold/init.py's compound two-line
[WARN] message.

The other ~183 print() calls stay as direct calls: they render final
CLI output (status lines, tables, "chock plugin build" summaries) in
argparse main()/*_main() functions across chock's *cli.py, install*,
scaffold/*, authoring/*, registry/*, plugin/* and validation/report.py
-- every file T201 flags turns out to be a genuine CLI/render surface,
which the next commit documents with per-file-ignores rather than
wrapping 183 call sites in indirection that changes nothing about
what gets printed.

Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Every one of the 183 remaining print() call sites lives in a file
that either dispatches an argparse subcommand (*_main()/main()
across cli.py, lifecycle.py, toggles.py, */cli.py, hooks/install.py,
scaffold/*, eval/cli.py), renders a report (validation/report.py,
gatelog.py), or is chock's own low-level installer/hook logic
printing operator-facing status. chock has no other consumer of these
strings -- there is no library layer separate from the CLI here, so
wrapping each call in an indirection layer would only add churn, not
change behaviour or add a real seam. Each per-file-ignore entry names
this once for the group, plus a one-off for the `chock new` skill
script template stub, whose print() is placeholder scaffold content,
not chock's own output.

lifecycle.py and scaffold/remove.py already carried a PLC0415
per-file-ignore; merged T201 into the same two entries rather than
duplicating the TOML key.

Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass. Remaining ruff findings:
37, all C901/PLR091x complexity.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
37 findings across 21 functions in 21 files (some functions trip
several of C901/PLR0911/PLR0912/PLR0913/PLR0915 at once). Every one
needs an actual function split or an argument-bundling refactor to
fix -- not a mechanical rewrite like every other category in this
wave -- and the brief calls that out explicitly as its own
behaviour-preservation-checked step, separate from turning the rule
set on and clearing what's safely automatable. Attempting 21 such
splits under this same pass risked exactly the kind of subtle
behaviour drift (a reordered branch, a changed early-return, a moved
side effect) the brief's gates exist to catch, for marginal lint
benefit -- most of these are barely over the line (11-16 against a
threshold of 10 or 12).

Used the brief's own escape hatch for an oversized baseline: each
per-file-ignore entry is scoped to only the specific rule codes that
file needs (not a blanket ignore), marked TODO(lint-adoption), and
comments the offending function name(s) so the entry is
self-documenting. Full enumeration with file:line detail also goes in
the w50 report on org-plan. `ruff check src/` is otherwise fully
clean: every other finding from the measured baseline (503 after
re-measuring against this branch's starting point, versus the brief's
525) is fixed, not deferred.

tests/, acceptance/ and tools/ still carry ~423 findings under the new
rule set beyond their existing S101/FBT/PLR2004 carve-out -- out of
scope here: the brief's "fix the baseline" mandate and its own
measured count (525 / re-measured 503) are both specifically about
src/, never the test suite.

Full suite green (1045 passed, 6 skipped); chock check, sync --check
and the literal-duplication check all pass; `ruff check src/` clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…ed_text

Compact, short-code-english block naming the rule families in select
(C90/N/PLR/PLW/PLC/ERA/T20/ARG/RET/SIM/PIE/FBT/A/B/S/BLE/TRY/RUF), the
numeric thresholds, the magic-value remedy and its own duplication
check, the boolean-keyword-only rule, and the per-file-ignore
discipline (a reason always, TODO(lint-adoption) as the sanctioned
escape hatch for an oversized baseline slice) -- matching the format
the owner's coding-standards doc already states.

Full suite green (1055 passed, 2 skipped -- more of the suite runs
now that the build package is installed in this environment,
unlocking wheel tests that were skipping for its absence); chock
check, sync --check and the literal-duplication check all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: f96d5b7c-8f84-4402-b293-079f17085c96


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/chock/output.py Fixed
Comment thread src/chock/output.py Fixed
Comment thread src/chock/validation/frontier.py Fixed
CI's validate job runs `ruff check .` across the whole repo, not just
src/ (which is all this PR's W50 baseline sweep measured and fixed).
Widening [tool.ruff.lint] select repo-wide therefore turned pre-existing,
untouched findings under tests/, acceptance/, docs/assets/, and fuzz/
into new failures on every validate job (3 Python versions).

Extend the existing tests/**/acceptance/** per-file-ignores and add
docs/assets/** and fuzz/** entries covering exactly the rule codes that
fire there today, marked TODO(lint-adoption) and enumerated by rule
family in the org-plan report, consistent with the brief's per-file-ignore
escape hatch for baseline too large for one PR. Drop one now-unused
S602 noqa in tests/test_copilot_hook_runtime.py made redundant by the
new tests/** ignore.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…Error rename

The N818 (error-suffix naming) fix earlier in this branch renamed
chock.policy_id.InvalidPolicyId to InvalidPolicyIdError, but fuzz/
is outside src/ so it wasn't caught by the same-tree grep at the
time. fuzz/fuzz_policy_id.py still imported and caught the old name,
so every fuzz CI run failed at import with ModuleNotFoundError-adjacent
ImportError before a single input ran.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…y except

Two "high" alerts (py/clear-text-logging-sensitive-data) fired on
output.py's warn()/error() -- CodeQL's name-based heuristic, not an
actual credential flow. Audited every call site in src/: each passes
a diagnostic string (an exception message, a file path, or an INDEX
token-budget count). The heuristic's likely trigger is call sites like
index/render.py's max_tokens/main_tokens (LLM context-window counts,
not auth tokens) and compile/emitters/in_agent.py's PROJECT_DIR_TOKEN,
a literal shell placeholder string already ruled non-sensitive at its
own definition (bandit S105). Documented the audit at the sink and
added the standard lgtm[query-id] suppression comment GitHub code
scanning honors for a reviewed false positive.

Also gave the "note"-severity empty except in validation/frontier.py
(a malformed/missing fetched_at timestamp, already deliberately
swallowed) its explanatory comment.

Neither change alters behaviour: comments only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Comment thread src/chock/output.py Fixed
Comment thread src/chock/output.py Fixed
…ment why

CI pins ruff>=0.16,<0.17 (pyproject.toml); the previous commit's local
verification ran against a stale 0.15.8 shadowing the pip-installed
0.16.5 on PATH. 0.16 added PLR0917 (too-many-positional-arguments),
which 0.15.8 never fires -- 10 findings appeared on re-scan with the
correct binary, split across already-deferred src/ complexity findings
(eval/derive.py's _case, gate/runner.py's run, plugin/store.py's three
builders -- added to their existing per-file-ignore entries) and the
same out-of-scope trees from the previous commit (tests/**,
docs/assets/**). Also corrected eval/derive.py's stale ignore comment:
the deferred PLR0913 function is _case, not build_case.

The `lgtm[py/clear-text-logging-sensitive-data]` suppression comments
added to output.py did not clear CodeQL's alerts on re-scan (new alerts
125/126 opened at the shifted line numbers; 122/123 just went outdated,
not resolved) -- documented that finding instead of repeating an
attempt already shown not to work, with the concrete traced source
(index/render.py's token-BUDGET counts, not credentials) and what
dismissing it needs that this session doesn't have.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Owner Author

All CI-blocking findings from the first push are fixed and pushed except one, which needs a maintainer action:

Fixed (commits d4f9cdf, 33434f6, 5964bc7):

  • ruff check . failing on all 3 Python versions: CI lints the whole repo, not just src/ (this wave's scope); widening [tool.ruff.lint] select therefore surfaced ~460 pre-existing, untouched findings under tests/, acceptance/, docs/assets/, and fuzz/. Scoped with per-file-ignores, each marked TODO(lint-adoption).
  • fuzz job: fuzz/fuzz_policy_id.py still imported chock.policy_id.InvalidPolicyId, renamed to InvalidPolicyIdError by this PR's N818 fix; fuzz/ is outside src/ so the earlier same-tree grep missed it.
  • A second round of ruff findings (PLR0917, too-many-positional-arguments) that only CI's pinned ruff==0.16.5 fires — this session's local ruff had drifted to a stale 0.15.8 that predates that rule. Re-verified against the correct pinned version; both ruff check . and ruff format --check . are clean under it now.

Not fixed — needs a maintainer with Security-tab access: CodeQL's py/clear-text-logging-sensitive-data on src/chock/output.py's warn()/error() (2 "high" alerts). I audited every call site in src/; none passes credential material. The one traced source is index/render.py's max_tokens/main_tokens — LLM context-window token-budget counts, not auth tokens — reaching index/cli.py's warn(output.warning); CodeQL's heuristic matches on the name, not the content. I added an inline lgtm[py/clear-text-logging-sensitive-data] suppression comment at both print sites and re-pushed; it did not clear the alert (the old alerts went "outdated", not resolved, and new ones opened at the shifted lines), so this repo's code-scanning setup isn't honoring that suppression syntax. I don't have Security-tab / alert-dismissal access from this session to mark it a false positive directly. Renaming the token-budget fields to dodge the heuristic would be a larger, riskier change than this PR's scope justifies on a naming hunch alone, so I left it as documented in src/chock/output.py and in the org-plan handback report rather than guessing further.

Everything else (validate x3, fuzz) should be green on the current head (5964bc7).


Generated by Claude Code

…t.py

The lgtm[...] suppression the prior commit tried is the legacy LGTM.com
syntax; GitHub code scanning's own alert-suppression directive is
# codeql[rule-id] on the line immediately above the flagged statement
(the same pattern already used in gate/sessionstart.py, chock#83). Applies
it to both flagged prints in output.py (py/clear-text-logging-sensitive-data
-- a name-based false positive on token-named diagnostic strings that are
LLM context-window counts, not credentials; every warn()/error() call site
in src/ was already audited and none carries credential material).

The suppression comment reads as commented-out code to ruff's new ERA001
(this wave's own rule), so it carries its own justified noqa alongside it,
matching this file's existing per-line noqa convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…1 goes file-scoped instead

The previous commit's # codeql[rule-id]  # noqa: ERA001 -- text on one line
did not clear the alert on re-scan (still flagged, same 2 alerts, confirmed
against the new head). CodeQL's AlertSuppression matcher for py/clear-text-
logging-sensitive-data most plausibly requires the comment line to end at
the suppression directive -- trailing text (even as a second # comment)
breaks the match. Dropped the trailing noqa; ERA001 is file-scoped in
pyproject.toml instead (output.py is two functions, each with a real
codeql[...] suppression comment above its print -- there is no other
commented-out code in this file for ERA001 to actually catch). T201 rides
the same file-scoped ignore, dropping the now-redundant per-line noqa.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Comment thread src/chock/output.py

def warn(message: str) -> None:
# codeql[py/clear-text-logging-sensitive-data]
print(f"[WARN] {message}", file=sys.stderr)
Comment thread src/chock/output.py

def error(message: str) -> None:
# codeql[py/clear-text-logging-sensitive-data]
print(f"[ERROR] {message}", file=sys.stderr)

Copy link
Copy Markdown
Owner Author

Status: CodeQL red, blocked on Security-tab access — needs the owner.

All 16 other checks are green on this PR (currently head f9eca6a). The one failure is 2 HIGH py/clear-text-logging-sensitive-data alerts on the new src/chock/output.py, a name-based false positive (see the file's own header comment for the full audit trail — every warn()/error() call site was checked; the traced source is index/render.py's max_tokens/main_tokens, LLM context-window counts, not credentials).

Two independent suppression attempts, both fully re-verified (full suite 1045/6, ruff check/format clean) before pushing:

  1. # codeql[rule-id] with a trailing # noqa: ERA001 on the same line — didn't clear.
  2. A clean single-line # codeql[rule-id] directive with ERA001 moved to a file-scoped pyproject.toml ignore instead — also didn't clear.

Both re-scans still report the alerts as "New alert" at the (shifted) print lines. This suggests inline alert-suppression may not be enabled for this repository at the GitHub Advanced Security config level — something this session has no way to check or change.

Leaving this in draft rather than guessing a third time. Needs one of: dismissing the alert by hand in the Security tab as a false positive, or confirming inline suppression is enabled so the syntax can be re-attempted with real UI feedback. Full detail in plan/spine-a/reports/w50.md and the org-plan ledger.

🤖 Generated with Claude Code


Generated by Claude Code


Generated by Claude Code

@open-coder-ai
open-coder-ai marked this pull request as ready for review September 2, 2026 09:04
@open-coder-ai
open-coder-ai merged commit 3daee21 into main Sep 2, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants